package com.xuchang.service;


import com.xuchang.constant.Constants;
import com.xuchang.domain.Line;
import com.xuchang.dto.CreateKLineDto;
import com.xuchang.enums.KlineType;
import com.xuchang.utils.KlineTimeUtil;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;


/**
 * TradeKlineService 类用于在交易完成（撮合）后生成K线数据。
 * 实现了 CommandLineRunner 接口，在应用启动时会自动执行 run 方法，
 * 从阻塞队列中获取交易数据，根据不同的K线类型生成对应的K线数据并存储到Redis中。
 */

@Service
public class TradeKlineService implements CommandLineRunner {
    // 当我们的交易完成(撮合)之后,就会触发k 线的生成
    public static BlockingDeque<CreateKLineDto> queue = new LinkedBlockingDeque();
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void run(String... args) throws Exception {
        while (true){
            CreateKLineDto createKLineDto = queue.poll();
            // 从里面获取一个数据,若没有数据,则会阻塞
            if (createKLineDto!=null) {
                for (KlineType klineType : KlineType.values()) {
                    this.generateKLine(createKLineDto,klineType);
                }
            }
        }
    }

    private void generateKLine(CreateKLineDto createKLineDto, KlineType klineType) {
        String redisKey = new StringBuilder(Constants.REDIS_KEY_TRADE_KLINE).append(createKLineDto.getSymbol().toLowerCase())
                .append(":").append(klineType.getValue().toLowerCase()).toString();
        //  获取之前该K 线的数据
        Long size = redisTemplate.opsForList().size(redisKey);
        DateTime kLineTime = KlineTimeUtil.getKLineTime(klineType);

        if (size == 0) {
            Line line = new Line(kLineTime, createKLineDto.getPrice(), createKLineDto.getVolume());
            redisTemplate.opsForList().leftPush(redisKey,line.toKline());
            return;
        }
        // 之前有数据 ,获取最近的一个数据
        String historyKlineData = redisTemplate.opsForList().range(redisKey, 0, -1).get(0);
        Line historyKline = new Line(historyKlineData);

        // 4若当前的时间: 是否还在上一个时间的区间内
        if (kLineTime.compareTo(historyKline.getTime()) == 1) {
            if (size> Constants.REDIS_MAX_CACHE_KLINE_SIZE) {
                redisTemplate.opsForList().rightPop(redisKey);
            }

            Line line = new Line();
            line.setTime(kLineTime);
            // 如果我们当前的交易量为 0
            if (createKLineDto.getVolume().compareTo(BigDecimal.ZERO) == 0) {
                line.setHigh(historyKline.getClose());
                line.setLow(historyKline.getClose());
                line.setOpen(historyKline.getClose());
                line.setClose(historyKline.getClose());
                line.setVolume(BigDecimal.ZERO) ; // 设置交易量为0
                // 放在Redis 里面
                redisTemplate.opsForList().leftPush(redisKey, line.toKline());
                return;
            }
            line.setOpen(createKLineDto.getPrice());
            line.setClose(createKLineDto.getPrice());
            // 最高价 ,最低价
            if (createKLineDto.getPrice().compareTo(historyKline.getHigh()) == 1) {
                line.setHigh(createKLineDto.getPrice());
                line.setLow(historyKline.getClose());
            }
            if (createKLineDto.getPrice().compareTo(historyKline.getLow()) == 1) {
                line.setLow(createKLineDto.getPrice());
                line.setHigh(historyKline.getClose());
            }

            historyKline.setClose(createKLineDto.getPrice());
            redisTemplate.opsForList().set(redisKey, 0, historyKline.toKline());
            // 放最新的进入
            // 放在Redis 里面
            redisTemplate.opsForList().leftPush(redisKey, line.toKline());
            return;
        }
        if (createKLineDto.getVolume().compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        historyKline.setClose(createKLineDto.getPrice());

        if (createKLineDto.getPrice().compareTo(historyKline.getHigh()) == 1) {
            historyKline.setHigh(createKLineDto.getPrice());
        }

        if (createKLineDto.getPrice().compareTo(historyKline.getLow()) == 1) {
            historyKline.setLow(createKLineDto.getPrice());
        }
        historyKline.setVolume(historyKline.getVolume().add(createKLineDto.getVolume()));
        redisTemplate.opsForList().set(redisKey, 0, historyKline.toKline());
    }
}

